package com.fingard.net.ftp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Date;

import com.fingard.constant.Format;
import com.fingard.diagnostics.LogHelper;
import com.fingard.dsp.bank.directConfig.FtpSet;
import com.fingard.net.ftp.BizFtpSocket.ENFtpDataType;
import com.fingard.net.ftp.BizFtpSocket.ENFtpSocketType;
import com.fingard.text.StringHelper;
import com.fingard.util.DateHelper;

import com.fingard.FGBiz;

public class BizFtpClient {

	public String ServerHostPort = "";
    //private IPAddress _ServerIPAddress;
    private String ip = "";
    private int _Port = 0;
    private String _FtpUser = "";
    private String _FtpPassword = "";
    
    private FtpSet ftpSet;
    public StringBuilder sbLog;
    public BizFtpClient(FtpSet pSet){
    	ftpSet = pSet.clone();
    	_FtpUser = pSet.userName;
    	_FtpPassword = pSet.userPassword;
    	ip = pSet.serverIP;
    	_Port = pSet.port;
    	_Encoding = pSet.charset;
    	if(StringHelper.isNullOrEmpty(_Encoding)){
    		_Encoding = "utf-8";
    	}
    	CurrentTypeAI = "";
    	_IsLogin = false;
    	_ControlSocket = new BizFtpSocket(ENFtpSocketType.ControlSocket, ENFtpDataType.Content);
        _ControlSocket.ServerHostPort = ServerHostPort;
    	sbLog = new StringBuilder();
    }

    public BizFtpClient(String p_ServerAddress, String p_FtpUser, String p_FtpPassword)
    {
        ServerHostPort = p_ServerAddress;
        p_ServerAddress = StringHelper.trimStart(p_ServerAddress, "ftp://");
        if (p_ServerAddress.indexOf("/") > 0)
        {
            p_ServerAddress = p_ServerAddress.substring(0, p_ServerAddress.indexOf("/"));
        }
        
        //IPAddress tmpIPAddress = null;
        int tmpPort = 21;
        if (p_ServerAddress.indexOf(":") > 0)
        {
            String[] tmpAddrParts = p_ServerAddress.split(":");
            ip = tmpAddrParts[0];
            tmpPort = Integer.parseInt(tmpAddrParts[1]);
        }
        else
        {
        	if(p_ServerAddress.matches("^\\d+([\\.\\d])+$")){
        		ip = p_ServerAddress;
        	}else{
        		ip = p_ServerAddress;
        	}
        }
        

        init(ip, tmpPort, p_FtpUser, p_FtpPassword);
    }

    public BizFtpClient(String p_ServerIP, int p_Port, String p_FtpUser, String p_FtpPassword)
    {
        ServerHostPort = p_ServerIP + ":" + p_Port;
        //IPAddress tmpIPAddress = IPAddress.Parse(p_ServerIP);
        init(p_ServerIP, p_Port, p_FtpUser, p_FtpPassword);
    }
    private BizFtpSocket _ControlSocket;

    private boolean _IsLogin;
    public boolean isLogin(){
    	return _IsLogin;
    }
    
    public String _Encoding = "";
    public String CurrentTypeAI;
    
    private void init(String p_ServerIP, int p_Port, String p_FtpUser, String p_FtpPassword)
    {
        ip = p_ServerIP;
        _Port = p_Port;
        _FtpUser = p_FtpUser;
        _FtpPassword = p_FtpPassword;
        _IsLogin = false;
        _Encoding = "utf-8";
        CurrentTypeAI = "";
        _ControlSocket = new BizFtpSocket(ENFtpSocketType.ControlSocket, ENFtpDataType.Content);
        _ControlSocket.ServerHostPort = ServerHostPort;
    }
    private boolean IsVsFTP = false;
    
    private void appendLog(String p_log){
    	sbLog.append(p_log+"\r\n");
    }

    public void connect() throws Exception{
        //IPEndPoint tmpEndPoint = new IPEndPoint(_ServerIPAddress, _Port);
        try{
        	sbLog.append("连接" + ftpSet.serverIP+":"+ftpSet.port+"\r\n");
        	_ControlSocket.FtpSocket = new Socket(ftpSet.serverIP, ftpSet.port);
        	_ControlSocket.FtpSocket.setSoTimeout(20000);
        }catch (Exception ex){
            _ControlSocket.close();
            _ControlSocket = null;
            appendLog(LogHelper.getStackTrace(ex));
            throw new Exception("连接服务器失败：" + LogHelper.getStackTrace(ex));
        }

        try{
            BizFtpResp resp = receiveControl();
            if (resp.msg.contains("vsFTP"))
            {
                IsVsFTP = true;
                appendLog("IsVsFTP：" + IsVsFTP);
            }
            if (!"220".equals(resp.statusCode))
            {
                this.close();
                throw new Exception(resp.msg);
            }
        }
        catch (Exception ex)
        {
        	String exMsg = LogHelper.getStackTrace(ex);
        	appendLog(exMsg);
            this.close();
            throw new Exception("连接服务器失败：" + exMsg);
        }
    }
    
    public void close(){
        if (_ControlSocket != null){
            try
            {
                quit();
            }catch (Exception ex){
            	appendLog(LogHelper.getStackTrace(ex));
            }
            _IsLogin = false;
            try{
            	if(_ControlSocket!=null){
            		_ControlSocket.close();
            		_ControlSocket = null;
            	}
            }catch (Exception ex){
            	appendLog(LogHelper.getStackTrace(ex));
            }
        }
    }
    

    
    private final int MaxTriedCountRecZero = 5;

    private BizFtpResp receiveControl() throws Exception
    {
        return receiveControl(-1);
    }

    private BizFtpResp receiveControl(int p_WaitMilSec) throws Exception
    {
    	BizFtpResp resp = new BizFtpResp();
        _ControlSocket.ReInit();
        receiveSocketStream(_ControlSocket);

        resp.msg = new String(_ControlSocket.ReceivedBytes, _Encoding);
        appendLog("接收：" + resp.msg.trim());
        if (_ControlSocket.IsCompleted){
            if (resp.msg.indexOf("\n") != resp.msg.lastIndexOf("\n")){
                String[] tmpMsgList = resp.msg.split("\n");
                resp.msg = tmpMsgList[tmpMsgList.length - 1];
                if (resp.msg.length() >= 3){//&& tmpRecMsg.Substring(3, 1) == " "
                	resp.statusCode = resp.msg.substring(0, 3);
                }
            }else if (resp.msg.length() >= 3){//&& tmpRecMsg.Substring(3, 1) == " "
            	resp.statusCode = resp.msg.substring(0, 3);
            }
            return resp;
        }
        else
        {
            throw new Exception("接收服务器返回消息未正常结束，已接收内容：" + resp.msg);
        }
    }
    /**\r*/
    private static byte headCut1Cr = '\r';//"\\r".getBytes("gbk")[0];
    /**\n*/
    private static byte headCut2Lf = '\n';//"\\n".getBytes("gbk")[0];
    
    private void receiveSocketStream(BizFtpSocket p_socket)
    {
        BizFtpSocket tmpBizSocket = null;
        try
        {
        	tmpBizSocket = p_socket;
        	InputStream inStream = tmpBizSocket.FtpSocket.getInputStream();
        	int readTimes = 0;
        	int tmpReadLen = 0;
        	while(readTimes==0 || tmpReadLen>0){
        		if(tmpBizSocket.IsCompleted){
        			break;
        		}
        		readTimes++;
        		tmpReadLen = inStream.read(tmpBizSocket.BufferBytes, 0, tmpBizSocket.BufferBytes.length);
                if (tmpReadLen > 0){
                    tmpBizSocket.RecByteLength += tmpReadLen;
                    tmpBizSocket.LastRecTime = new Date();
                    double diffSecs = DateHelper.getDifferSecs(tmpBizSocket.LastRecTime, tmpBizSocket.LastLogTime);
                    if (diffSecs >= 30 || (tmpBizSocket.getProgressDownFile() - tmpBizSocket.LastLogProgressDownFile > 0.1)){
                    	diffSecs = DateHelper.getDifferSecs(new Date(), tmpBizSocket.BeginRecTime);
                        tmpBizSocket.LastLogProgressDownFile = tmpBizSocket.getProgressDownFile();
                        if (tmpBizSocket.LastLogProgressDownFile > 0){
                            this.appendLog("进度" + tmpBizSocket.getProgressDownFilePercent() + "，已耗时" + Format.DecimalAmtFormat.format(diffSecs) + "秒，总字节数：" + tmpBizSocket.FileSize + "，已接收字节数：" + tmpBizSocket.RecByteLength);
                        }
                        else{
                        	appendLog("已耗时" + Format.DecimalAmtFormat.format(diffSecs) + "秒" + "，已接收字节数：" + tmpBizSocket.RecByteLength);
                        }
                        tmpBizSocket.LastLogTime = new Date();
                    }
                    if (tmpBizSocket.FtpDataType == ENFtpDataType.DownloadFile){
                        tmpBizSocket.saveToDestFileStream(tmpReadLen);
                        if (tmpBizSocket.FileSize > 0 && tmpBizSocket.RecByteLength == tmpBizSocket.FileSize)
                        {
                            tmpBizSocket.closeDestFileStream();
                            tmpBizSocket.IsCompleted = true;
                            diffSecs = DateHelper.getDifferSecs(new Date(), tmpBizSocket.BeginRecTime);
                            appendLog("字节数：" + tmpBizSocket.RecByteLength + "，总耗时" + Format.DecimalAmtFormat.format(diffSecs) + "秒");
                        }
                    }
                    else{
                        byte[] tmpUnionInfo = new byte[tmpBizSocket.ReceivedBytes.length + tmpReadLen];
                        System.arraycopy(tmpBizSocket.ReceivedBytes, 0, tmpUnionInfo, 0, tmpBizSocket.ReceivedBytes.length);
                        System.arraycopy(tmpBizSocket.BufferBytes, 0, tmpUnionInfo, tmpBizSocket.ReceivedBytes.length, tmpReadLen);
                        tmpBizSocket.ReceivedBytes = tmpUnionInfo;

                        if (tmpBizSocket.FtpSocketType == ENFtpSocketType.ControlSocket){
                            if (tmpBizSocket.ReceivedBytes.length > 1){
                                int tmpCmpIndex = tmpBizSocket.ReceivedBytes.length - 1;
                                if (tmpBizSocket.ReceivedBytes[tmpCmpIndex - 1] == headCut1Cr && tmpBizSocket.ReceivedBytes[tmpCmpIndex] == headCut2Lf){
                                    tmpBizSocket.IsCompleted = true;
                                }
                            }
                        }
                    }
                }else{
                    if (tmpBizSocket.FtpSocketType == ENFtpSocketType.DataSocket){
                        tmpBizSocket.closeDestFileStream();
                        tmpBizSocket.IsCompleted = true;
                        double diffSecs = DateHelper.getDifferSecs(new Date(), tmpBizSocket.BeginRecTime);
                        this.appendLog("接收字节数：" + tmpBizSocket.RecByteLength + "，总耗时" + Format.DecimalAmtFormat.format(diffSecs) + "秒");
                    }
                    else{
                        tmpBizSocket.RecZeroCount = tmpBizSocket.RecZeroCount + 1;
                        appendLog("接收字节数为0，次数：" + tmpBizSocket.RecZeroCount);
                        double diffSecs = DateHelper.getDifferSecs(new Date(), tmpBizSocket.LastRecTime);
                        if (tmpBizSocket.RecZeroCount > MaxTriedCountRecZero || diffSecs > 30){//30秒后如果接收的数据还是0
                            break;
                        }
                    }
                }
        	}
        }catch (Exception ex){
        	appendLog(LogHelper.getStackTrace(ex));
        }
    }
    
    public void login() throws Exception{
    	BizFtpResp resp = sendControl("USER " + _FtpUser);
        String tmpStatusCode = resp.statusCode;
        String tmpRecMsg = resp.msg;
        
        if ("331".equals(tmpStatusCode) || "230".equals(tmpStatusCode))
        {
            if (!"230".equals(tmpStatusCode))
            {
            	resp = sendControl("PASS " + _FtpPassword);
            	tmpStatusCode = resp.statusCode;
            	tmpRecMsg = resp.msg;
            }

            for (int i = 0; i < 30; i++)
            {
                if (tmpRecMsg.indexOf("230-") >= 0 && tmpRecMsg.indexOf("230 ") < 0)//
                {
                	resp = receiveControl();
                	tmpStatusCode = resp.statusCode;
                	tmpRecMsg = resp.msg;
                }
                else
                {
                    break;
                }
            }

            if ("230".equals(tmpStatusCode) || "202".equals(tmpStatusCode))
            {
                _IsLogin = true;
            }
            else
            {
                throw new Exception(tmpRecMsg);
            }
        }
        else
        {
            throw new Exception(tmpRecMsg);
        }
    }
    
    public void connectAndLogin() throws Exception{
        connect();
        login();
    }
    
    public void setTypeToI() throws Exception
    {
        setType("I");
    }
    public void setTypeToA() throws Exception
    {
        setType("A");
    }
    private void setType(String p_StrType) throws Exception
    {
        if (CurrentTypeAI.equals(p_StrType))
        {
        	BizFtpResp resp = sendControl("TYPE " + p_StrType);
            String tmpStatusCode = resp.statusCode;
            String tmpRecMsg = resp.msg;
            if ("226".equals(tmpStatusCode))//226 Transfer complete.是上次传输成功的消息，省略
            {
            	resp = receiveControl();
            	tmpStatusCode = resp.statusCode;
                tmpRecMsg = resp.msg;
            }
            if (!"200".equals(tmpStatusCode))
            {
                throw new Exception(tmpRecMsg);
            }
            else
            {
                CurrentTypeAI = p_StrType;
            }
        }
    }
    
    public String pwd() throws Exception
    {
        BizFtpResp resp = sendControl("PWD");
        String tmpStatusCode = resp.statusCode;
        String tmpRecMsg = resp.msg;
        
        if ("226".equals(tmpStatusCode))//226 Transfer complete.是上次传输成功的消息，省略
        {
        	resp =  receiveControl();
        	tmpStatusCode = resp.statusCode;
            tmpRecMsg = resp.msg;
        }
        if (!"257".equals(tmpStatusCode))
        {
            throw new Exception(tmpRecMsg);
        }
        else
        {
            String tmpCurrDir = "";
            if (tmpRecMsg.indexOf("\"") > 0)
            {
                int tmpIndex1 = tmpRecMsg.indexOf("\"") + 1;
                if (tmpRecMsg.length() > tmpIndex1)
                {
                    tmpCurrDir = tmpRecMsg.substring(tmpIndex1);
                    int tmpIndex2 = tmpCurrDir.indexOf("\"");
                    if (tmpIndex2 > 0)
                    {
                        tmpCurrDir = tmpCurrDir.substring(0, tmpIndex2);
                    }
                }
                return tmpCurrDir;
            }
            else
            {
                tmpCurrDir = StringHelper.trimStart(tmpRecMsg, "257 ").trim();
                if (tmpCurrDir.indexOf(' ') > 0)
                {
                    tmpCurrDir = StringHelper.trimStartAndEnd(tmpCurrDir, "\"");
                }
                return tmpCurrDir;
            }
        }
    }
    
    public void cwd(String p_DirName) throws Exception{
        String tmpStatusCode = "";
        String tmpRecMsg = "";
        if (IsVsFTP)
        {
            p_DirName = StringHelper.trimEndFileSp(p_DirName);
            if (p_DirName.length() == 0)
            {
                p_DirName = "/";
            }
        }
        BizFtpResp resp = sendControl("CWD " + p_DirName);
        tmpStatusCode = resp.statusCode;
        tmpRecMsg = resp.msg;
        
        if ("226".equals(tmpStatusCode))//226 Transfer complete.是上次传输成功的消息，省略
        {
        	resp = receiveControl();
        	tmpStatusCode = resp.statusCode;
            tmpRecMsg = resp.msg;
        }
        else if (tmpRecMsg.startsWith("550-"))
        {
            //String tmpStatusCode2 = "";
            //String tmpRecMsg2 = receiveControl(2000);
            BizFtpResp resp2 = receiveControl(2000);
            this.appendLog(resp2.statusCode+" " +resp2.msg);
        }
        if (!"250".equals(tmpStatusCode))
        {
            throw new Exception(tmpRecMsg);
        }
    }
    
    public boolean createDirectory(String p_FileName, BizFtpResp resp) throws Exception
    {
        
        if (IsVsFTP)
        {
            p_FileName = StringHelper.trimEndFileSp(p_FileName);
            String parentDir = p_FileName;
            String newDir = "";
            if (p_FileName.lastIndexOf("/") > 0)
            {
                parentDir = p_FileName.substring(0, p_FileName.lastIndexOf("/"));
                newDir = p_FileName.substring(p_FileName.lastIndexOf("/") + 1);
            }
            else
            {
                parentDir = "/";
                newDir = StringHelper.trimStartFileSp(p_FileName);
            }

            if (parentDir.length() == 0)
            {
                parentDir = "/";
            }
            String tmpNowDir = pwd();
            if (!StringHelper.trimEndFileSp(tmpNowDir).equalsIgnoreCase(StringHelper.trimEndFileSp(parentDir)))
            {
                cwd(parentDir);
            }
            resp = sendControl("MKD " + newDir);
        }
        else
        {
        	resp = sendControl("MKD " + p_FileName);
        }
        if ("250".equals(resp.statusCode) || "257".equals(resp.statusCode))
        {
            return true;
        }
        else if ("550".equals(resp.statusCode))
        {
            if (resp.msg.contains("already exist") || resp.msg.contains("已经存在") || resp.msg.contains("已存在"))
            {
                return true;
            }
            return false;
        }
        else
        {
            throw new Exception(resp.msg);
        }

    }
    
    public BizFtpResp stat() throws Exception{
        return sendControl("STAT ");
    }
    
    
  /// <summary>
    /// 返回-1表示失败，返回1表示成功
    /// </summary>
    /// <param name="p_UpBytes"></param>
    /// <param name="p_UpDestFile"></param>
    /// <param name="p_OutMsg"></param>
    /// <returns></returns>
    public int uploadFile(byte[] p_UpBytes, String p_UpDestDir, String p_UpDestFile, BizFtpResp resp) throws Exception{
    	//resp = new BizFtpResp();
        String tmpNowDir = "";

        try{
            tmpNowDir = pwd();
        }
        catch (Exception ex){
        	resp.msg = "上传失败：上传前执行命令“PWD”出现异常：" + LogHelper.getStackTrace(ex);
            FGBiz.limitMsg.exception("BizFtp", ex);
            return -1;
        }

        if (!StringHelper.trimEndFileSp(tmpNowDir).equalsIgnoreCase(StringHelper.trimEndFileSp(p_UpDestDir))){
            try{
                p_UpDestDir = StringHelper.trimEndFileSp(p_UpDestDir) + "/";
                cwd(p_UpDestDir);
            }
            catch (Exception ex){
            	resp.msg = "上传失败：上传前执行命令“CWD " + p_UpDestDir + "”出现异常：" + LogHelper.getStackTrace(ex);
            	FGBiz.limitMsg.exception("BizFtp", ex);
                return -1;
            }
        }

        try{
            setTypeToI();
        }
        catch (Exception ex){
        	resp.msg = "上传失败：上传前执行命令“TYPE I”出现异常：" + LogHelper.getStackTrace(ex);
        	FGBiz.limitMsg.exception("BizFtp", ex);
            return -1;
        }

        BizFtpSocket tmpBizSocket = null;
        try
        {
            tmpBizSocket = createDataSocketPASV(ENFtpDataType.UploadFile);
        }
        catch (Exception ex)
        {
        	resp.msg = "上传失败：上传前执行命令“PASV”出现异常：" + LogHelper.getStackTrace(ex);
        	FGBiz.limitMsg.exception("BizFtp", ex);
            return -1;
        }

        if (tmpBizSocket == null)
        {
        	resp.msg = "上传失败：上传前执行命令“PASV”失败";
            return -1;
        }

        boolean tmpIsSTORSucc = false;
        try
        {
            //STOR AL11100672013051701.xml
            resp = sendControl("STOR " + p_UpDestFile);
            if ("226".equals(resp.statusCode))//226 Transfer complete.是上次传输成功的消息，省略
            {
            	resp = receiveControl();
            }
            tmpIsSTORSucc = true;
        }
        catch (Exception ex){
        	resp.statusCode = "";
        	resp.msg = "上传失败：上传前执行命令“STOR " + p_UpDestFile + "”出现异常：" + LogHelper.getStackTrace(ex);
        	FGBiz.limitMsg.exception("BizFtp", ex);
        }

        try
        {
            if (tmpIsSTORSucc == false)
            {
                return -1;
            }
            if ("150".equals(resp.statusCode) || "125".equals(resp.statusCode))
            {
                //"上传字节"
                int tmpOffset = 0;
                int tmpEachSize = 1024 * 1024;
                OutputStream outStream = tmpBizSocket.FtpSocket.getOutputStream();//.write(tmpSendBytes);
                while (tmpOffset < p_UpBytes.length)
                {
                    if (p_UpBytes.length > tmpOffset + tmpEachSize)
                    {
                        tmpEachSize = 1024 * 1024;
                    }
                    else
                    {
                        tmpEachSize = p_UpBytes.length - tmpOffset;
                    }
                    outStream.write(p_UpBytes, tmpOffset, tmpEachSize);
                    tmpOffset += tmpEachSize;
                }
                tmpBizSocket.close();
                tmpBizSocket = null;
                resp = receiveControl();
                return 1;
            }
            else
            {
            	resp.msg = "上传失败：上传前执行命令“STOR " + p_UpDestFile + "”失败：" + resp.msg;
                return -1;
            }
        }
        catch (Exception ex)
        {
        	FGBiz.limitMsg.exception("BizFtp", ex);
            throw (ex);
        }
        finally
        {
            if (tmpBizSocket != null)
            {
                tmpBizSocket.close();
                tmpBizSocket = null;
            }
        }
    }
    
    private BizFtpSocket createDataSocketPASV(ENFtpDataType p_FtpDataType) throws Exception{
    	BizFtpResp resp = sendControl("PASV");
        if ("226".equals(resp.statusCode)){//226 Transfer complete.是上次传输成功的消息，省略
        	resp = receiveControl();
        }
        if ("227".equals(resp.statusCode)){//227 Entering Passive Mode (127,0,0,1,4,87).
            int tmpStartIndex = resp.msg.indexOf('(') + 1;
            int tmpEndIndex = resp.msg.indexOf(')');
            String tmpIP_Port = resp.msg.substring(tmpStartIndex, tmpEndIndex);
            String[] tmpAddrParts = tmpIP_Port.split(",");
            String tmpSrIP = tmpAddrParts[0] + "." + tmpAddrParts[1] + "." + tmpAddrParts[2] + "." + tmpAddrParts[3];
            int tmpSrPort = (Integer.parseInt(tmpAddrParts[4]) << 8) + Integer.parseInt(tmpAddrParts[5]);

            BizFtpSocket tmpBizSocket = new BizFtpSocket(ENFtpSocketType.DataSocket, p_FtpDataType);
            
            try
            {
            	tmpBizSocket.FtpSocket = new Socket(tmpSrIP, tmpSrPort);
                tmpBizSocket.FtpSocket.setSoTimeout(20000);//超时20秒
                tmpBizSocket.ServerHostPort = tmpSrIP + ":" + tmpSrPort;
            }
            catch (Exception ex){
                try {
					tmpBizSocket.close();
				} catch (IOException e) {
					FGBiz.limitMsg.exception("BizFtp", ex, "close " + tmpSrIP + ":" + tmpSrPort);
				}
                tmpBizSocket = null;
                FGBiz.limitMsg.exception("BizFtp", ex, tmpSrIP + ":" + tmpSrPort);
                throw (ex);
            }
            return tmpBizSocket;
        }
        else{
            throw new Exception(resp.msg);
        }
    }
    

    
    public void quit() throws Exception
    {
        //String tmpStatusCode = string.Empty;
        sendControl("QUIT");
        _IsLogin = false;
    }
    
    private BizFtpResp sendControl(String p_StrMsg) throws Exception
    {
        if (p_StrMsg.charAt(p_StrMsg.length() - 1) != headCut2Lf)//
        {
            p_StrMsg += "\r\n";
        }
        byte[] tmpSendBytes = p_StrMsg.getBytes(_Encoding);

        appendLog("发送：" + p_StrMsg.trim());
        _ControlSocket.FtpSocket.getOutputStream().write(tmpSendBytes);

        return receiveControl();
    }
}
